package com.sunshine.scw.base.dao.impl;

import java.io.Serializable;
import java.lang.reflect.ParameterizedType;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import org.hibernate.Criteria;
import org.hibernate.FetchMode;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.criterion.Criterion;
import org.hibernate.criterion.Order;
import org.hibernate.criterion.Projections;
import org.springframework.beans.factory.annotation.Autowired;

import com.sunshine.scw.base.dao.GenericDao;
import com.sunshine.scw.base.dao.Pagination;
import com.sunshine.scw.base.dao.query.QueryUtil;

/**
 * 泛型DAO实现类
 * 
 * @author xiao
 * 
 * @version 1.2
 * @param <T>
 *            泛型
 * @param <ID>
 *            ID
 */
public class GenericDaoImpl<T, ID extends Serializable> implements
		GenericDao<T, ID> {

	private Class<T> persistentClass;

	@Autowired
	private SessionFactory sessionFactory;

	public Session getSession() {
		return sessionFactory.getCurrentSession();
	}

	@SuppressWarnings("unchecked")
	protected GenericDaoImpl() {
		if (null == persistentClass) {
			// 通过反射机制得到泛型中的T.class
			this.persistentClass = (Class<T>) ((ParameterizedType) getClass()
					.getGenericSuperclass()).getActualTypeArguments()[0];
		}
	}

	public Class<T> getPersistentClass() {
		return persistentClass;
	}

	public void setPersistentClass(Class<T> persistentClass) {
		this.persistentClass = persistentClass;
	}

	@Override
	public void delete(T entity) {
		getSession().delete(entity);
	}

	@Override
	public void deleteById(ID id) {
		this.delete(this.findById(id));
	}

	@Override
	public T findById(ID id) {
		return (T) getSession().get(persistentClass, id);
	}

	@Override
	public Serializable save(T entity) {
		return getSession().save(entity);
	}

	@Override
	public void saveOrUpdate(T entity) {
		getSession().saveOrUpdate(entity);
	}

	@Override
	public void update(T entity) {
		getSession().update(entity);
	}

	@Override
	public Integer getRowCount() {
		return statistics();
	}

	@Override
	public void removeAll(Collection<T> entities) {
		for (T t : entities) {
			getSession().delete(t);
		}
	}

	@Override
	public Pagination<T> findPaginationInfo(int pageNo, int pageSize) {
		return findPaginationInfo(new QueryUtil<T>().addPaginationInfo(pageNo,
				pageSize));
	}

	@SuppressWarnings("unchecked")
	@Override
	public Pagination<T> findPaginationInfo(final QueryUtil<T> queryUtil) {
		final Pagination<T> pagination = new Pagination<T>();
		Session session = getSession();
		Criteria cta = session.createCriteria(persistentClass);
		List<Criterion> ctons = queryUtil.criterions();
		Pagination<T> pagin = queryUtil.getPagination();
		if (null != pagin) {
			int pageNo = pagin.getCurrentPage();
			int pageSize = pagin.getPageSize();
			cta.setFirstResult((pageNo - 1) * pageSize);
			cta.setMaxResults(pageSize);
			int totalCount = statistics(ctons);
			pagination.setCurrentPage(pageNo);
			pagination.setPageSize(pageSize);
			pagination.setTotalCount(totalCount);
		}
		Map<String, FetchMode> queryFetchModes = queryUtil.getFetchModes();
		int queryFetchModesSize = queryFetchModes.size();
		if (null != queryFetchModes && queryFetchModesSize > 0) {
			Iterator<Entry<String, FetchMode>> it = queryFetchModes.entrySet()
					.iterator();
			while (it.hasNext()) {
				Entry<String, FetchMode> entry = it.next();
				cta.setFetchMode(entry.getKey(), entry.getValue());
			}
		}
		if (null != ctons && ctons.size() > 0)
			for (int i = 0; i < ctons.size(); i++) {
				cta.add(ctons.get(i));
			}
		List<Order> orderList = queryUtil.getOrders();
		if (null != orderList && orderList.size() > 0) {
			for (Order order : orderList) {
				cta.addOrder(order);
			}
		}
		List<T> list = cta.list();
		pagination
				.setRecordList(list.size() <= 0 || list == null ? new ArrayList<T>()
						: list);
		return pagination;
	}

	@Override
	public List findAll(final QueryUtil<T> queryUtil) {
		Session session = getSession();
		Criteria cta = session.createCriteria(persistentClass);
		List<Criterion> ctons = queryUtil.criterions();
		if (null != ctons && ctons.size() > 0) {
			for (int i = 0; i < ctons.size(); i++) {
				Criterion cton = ctons.get(i);
				if (null != cton)
					cta.add(cton);
			}
		}
		Map<String, FetchMode> queryFetchModes = queryUtil.getFetchModes();
		int queryFetchModesSize = queryFetchModes.size();
		if (null != queryFetchModes && queryFetchModesSize > 0) {
			Iterator<Entry<String, FetchMode>> it = queryFetchModes.entrySet()
					.iterator();
			while (it.hasNext()) {
				Entry<String, FetchMode> entry = it.next();
				cta.setFetchMode(entry.getKey(), entry.getValue());
			}
		}
		List<Order> orderList = queryUtil.getOrders();
		if (null != orderList && orderList.size() > 0) {
			for (Order order : orderList) {
				cta.addOrder(order);
			}
		}
		return cta.list();
	}

	@Override
	public List findAll() {
		return findByCriteria();
	}

	@Override
	public List findByCriteria(final Criterion... criterion) {
		Session session = getSession();
		Criteria criteria = session.createCriteria(persistentClass);
		for (Criterion c : criterion) {
			if (c != null) {
				criteria.add(c);
			}
		}
		return criteria.list();
	}

	@Override
	public int statistics(Criterion... ctons) {
		int count = 0;
		Session session = getSession();
		Criteria cta = session.createCriteria(persistentClass);
		for (Criterion criterion : ctons) {
			if (null != ctons) {
				cta.add(criterion);
			}
		}
		count = ((Integer) (cta.setProjection(Projections.rowCount())
				.uniqueResult())).intValue();
		return count;
	}

	@Override
	public int statistics(List<Criterion> ctons) {
		Criterion[] criterions = null;
		if (ctons.size() > 0) {
			criterions = new Criterion[ctons.size()];
			for (int i = 0; i < criterions.length; i++) {
				criterions[i] = ctons.get(i);
			}
		}
		return statistics(criterions);
	}

}
